/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package common

import (
	"fmt"
	"strconv"

	"hundsun.com/hsl/hschain/common/util/pb"
	consensuscomm "hundsun.com/hsl/hschain/consensus/common"
	"hundsun.com/hsl/hschain/protos/consensus/dpos"
)

const (
	// LogFieldRound 轮次日志打印关键字
	LogFieldRound = "rd"

	//InvalidRound 非法的轮次
	InvalidRound int64 = -1
)

// 共识相关参数
var (
	// MaxMinersCount 每轮矿工个数
	MaxMinersCount int

	// MinApproveMinerCount 最小批准出块矿工数
	MinApproveMinerCount int

	// MaxRejectMinerCount 最大拒绝出块矿工数
	MaxRejectMinerCount int

	// RoundInterval 轮次间间隔时间（秒）
	RoundInterval uint32

	//TimeSliceLength 单个时间片秒数
	TimeSliceLength int

	// MaxBlocksInTimeSlice 单个时间片内的出块数量
	MaxBlocksInTimeSlice int

	//EpochIntervalMillis 每一轮时间
	EpochIntervalMillis int64
)

var genesisBlockTime int64

var genesisEpoch int64

// ErrInvalidBlockTime 区块打包时间异常错误信息
var ErrInvalidBlockTime error

// Initialize 初始化dpos共识基本信息
func Initialize(client *consensuscomm.BaseClient) {
	consensusConfPayload := client.GetCurParam().GetConsensus().GetDposPayload()
	MaxMinersCount = int(consensusConfPayload.MinerCount)
	RoundInterval = consensusConfPayload.RoundInterval
	TimeSliceLength = int(consensusConfPayload.TimeSliceLength)
	MaxBlocksInTimeSlice = int(consensusConfPayload.MaxBlocksInTimeSlice)
	EpochIntervalMillis = (int64(TimeSliceLength)*int64(MaxMinersCount) + int64(RoundInterval)) * 1000
	genesisBlock := pb.GetGenesisBlock()
	genesisBlockTime = genesisBlock.Header.BlockTime
	genesisEpoch = genesisBlockTime / EpochIntervalMillis

	MinApproveMinerCount = 2 * MaxMinersCount / 3
	MaxRejectMinerCount = MaxMinersCount / 3
	ErrInvalidBlockTime = fmt.Errorf("block time cannot be less than genesis block time %d", genesisBlockTime)
}

// CalculateRound 计算论次。如果传入的时间小于创世块时间，则报错ErrInvalidBlockTime
func CalculateRound(currentUnixMilli int64) (round int64, startUnixMilli int64, err error) {
	if currentUnixMilli < genesisBlockTime {
		return InvalidRound, 0, ErrInvalidBlockTime
	}
	currentEpoch := currentUnixMilli / EpochIntervalMillis
	return currentEpoch - genesisEpoch, currentEpoch * EpochIntervalMillis, nil
}

// CalculateTimeSlice 计算时间片
func CalculateTimeSlice(round int64, minerIndex int) (startUnixMilli, endUnixMilli int64) {
	if round < 0 {
		return 0, 0
	}
	startUnixMilli = (genesisEpoch+round)*EpochIntervalMillis + int64(minerIndex)*int64(TimeSliceLength)*1000
	endUnixMilli = startUnixMilli + int64(TimeSliceLength)*1000
	return
}

// CalculateProducePeriod 计算出块时间段（收尾都需要预留一点时间给下个生产者）
func CalculateProducePeriod(round int64, minerIndex int) (startUnixMilli, endUnixMilli int64) {
	startUnixMilli = (genesisEpoch+round)*EpochIntervalMillis + int64(minerIndex)*int64(TimeSliceLength)*1000
	endUnixMilli = startUnixMilli + int64(TimeSliceLength)*1000
	return startUnixMilli + 100, endUnixMilli - 100
}

// GenerateRoundMinersKey 生成轮次miner信息key值
func GenerateRoundMinersKey(round int64) []byte {
	roundMinersKey := []byte(dpos.EsIndexRoundMiners + strconv.FormatInt(round, 10))
	return roundMinersKey
}
